package cn.isjinhao.se.sql.grammar.statement.update;

import cn.isjinhao.se.sql.function.evaluator.FunctionEvaluator;
import cn.isjinhao.se.sql.grammar.CurdStatement;
import cn.isjinhao.se.sql.grammar.GrammarErrorReporterDelegate;
import cn.isjinhao.se.sql.grammar.statement.AssignmentList;
import cn.isjinhao.se.sql.grammar.statement.Grouping;
import cn.isjinhao.se.sql.grammar.statement.StatementParser;
import cn.isjinhao.se.sql.grammar.statement.update.represention.Update;
import cn.isjinhao.se.sql.grammar.statement.update.visitor.SingleUpdateMetaInfoDetector;
import cn.isjinhao.se.sql.lexical.scan.TokenSequence;
import cn.isjinhao.se.sql.lexical.token.Token;
import cn.isjinhao.se.sql.lexical.token.TokenType;

/**
 * @Author ISJINHAO
 * @Date 2021/4/5 12:03
 */
public class UpdateParser extends StatementParser {

    public UpdateParser(TokenSequence tokenSequence, FunctionEvaluator functionEvaluator) {
        super(tokenSequence, functionEvaluator);
    }


    /**
     * update           ->  "update" IDENTIFIER "set" assignmentList whereSeg
     * whereSeg         ->  "where" logic
     *
     * assignmentList   ->  (IDENTIFIER "=" binaryArithmetic) ("," IDENTIFIER "=" binaryArithmetic)*
     *
     * logic            ->  comparison (("or" | "and") comparison)*
     * comparison       ->  binaryArithmetic ((">" | "<" | ">=" | "<=" | "!=" | "=" | "like" | "contains") binaryArithmetic)?
     * binaryArithmetic ->  unaryArithmetic (("+" | "-" | "*" | "/") unaryArithmetic)*
     * unaryArithmetic  ->  ("!"|"-") unaryArithmetic | primary
     * primary          ->  #{xxx} | ? | "true" | "false" | "null" | NUMBER | STRING | IDENTIFIER | function | "(" logic ")"
     * function         ->  functionName "(" binaryArithmetic? ("," binaryArithmetic)* ")"
     * functionName     ->  "date_to_timestamp" | "date_to_str" | "str_to_date" | "str_to_timestamp" | "timestamp_to_date" | "timestamp_to_str" | "now"
     */
    @Override
    public CurdStatement parse() {
        CurdStatement update = update();
        update.accept(new SingleUpdateMetaInfoDetector());
        return update;
    }

    private CurdStatement update() {
        consume(TokenType.UPDATE, GrammarErrorReporterDelegate.UPDATE_update_PARSE);

        Token tableName = tokenSequence.takeCur();
        if(!TokenType.IDENTIFIER.equals(tableName.getType())) {
            error(GrammarErrorReporterDelegate.UPDATE_update_PARSE);
        }
        tokenSequence.advance();

        consume(TokenType.SET, GrammarErrorReporterDelegate.UPDATE_update_PARSE);

        CurdStatement curdStatement = assignmentList();
        if(!(curdStatement instanceof AssignmentList)) {
            error(GrammarErrorReporterDelegate.UPDATE_update_PARSE);
        } else {
            AssignmentList assignmentList = (AssignmentList) curdStatement;
            if(assignmentList.getEntryList() == null || assignmentList.getEntryList().isEmpty()) {
                error(GrammarErrorReporterDelegate.UPDATE_update_PARSE);
            }
        }

        CurdStatement whereSeg = whereSeg();

        return new Update(tableName, curdStatement, whereSeg);
    }

    @Override
    protected CurdStatement primary() {
        CurdStatement primary = super.primary();
        if(primary != null) {
            return primary;
        }

        // "(" logic ")"
        if (tokenSequence.equalThenAdvance(TokenType.LEFT_PAREN)) {
            CurdStatement curdStatement = logic();
            consume(TokenType.RIGHT_PAREN, GrammarErrorReporterDelegate.UPDATE_primary_PARSE);
            return new Grouping(curdStatement);
        }

        error(GrammarErrorReporterDelegate.ALL_token_undefined_PARSE);
        return null;
    }


}
